/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1/commercial
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may use this file in compliance with the License.
 * Alternatively, if you can't copy with the License, namely if you do not
 * want to provide source code of your modifications to this file, you may
 * negotiate a commercial licence with the initial developer.
 * You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is COID.
 *
 * The Initial Developer of the Original Code is
 * PosAm.
 * Portions created by the Initial Developer are Copyright (C) 2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Brano Kemen
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include <stdio.h>
#include "plugin.h"
#include "server.h"
//#include "autoclient.h"
#include "coid/coid.h"

namespace coid {


////////////////////////////////////////////////////////////////////////////////
PluginCoid::~PluginCoid()
{
}


////////////////////////////////////////////////////////////////////////////////
opcd PluginCoid::update_service_list( dynarray< NODEREF<ServiceCoid> >& newsvc, AcceptSvcCbk* acc )
{
    //_module = LoadLibraryEx( _path, 0, 0 );
    uint nok=0;

    if( _module.is_valid() )
    {
        t_coid_get_sdir   __lsvc1, __lsvc2;
        __lsvc1 = (t_coid_get_sdir) _module.getFuncAddress( COID_GET_FN_NAME );
        __lsvc2 = (t_coid_get_sdir) _module.getFuncAddress( COID_GET_FN_NAME2 );

        if(!__lsvc1) {
            if(!__lsvc2)
                post_warning(0) << "entry point '" COID_GET_FN_NAME "' not found in device: " << _path << EOM;
        }
        else
            nok = load_services( __lsvc1, newsvc, acc );
    }

    return nok ? opcd(0) : opcd(ersNOT_READY);
}

////////////////////////////////////////////////////////////////////////////////
static const ClassRegister<ServiceDescriptor>* __get_sd()
{
    return &INTERFACE_REGISTER(ServiceDescriptor);
}

////////////////////////////////////////////////////////////////////////////////
void PluginCoid::run_services( dynarray< NODEREF<ServiceCoid> >& svc )
{
    for( uint i=0; i<svc.size(); ++i )
    {
        //force to build meta information now
        svc[i]->_desc._fnc_get_meta();

        if( svc[i]->_desc._flags & ServiceDescriptor::fAUTORUN )
            svc[i]->spawn_auto_instance();

        if( svc[i]->_desc._acceptor_port != UMAX32  &&  svc[i]->_desc._acceptor_port != 0 )
        {
            //create special acceptor
            SRVROOTA.start_acceptor( svc[i], svc[i]->_desc._acceptor_port );
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
opcd PluginCoid::reload()
{
    if( _module.is_valid() )
    {
        opcd e = SRVROOTA.detach( _id, 0, true );
        if(e)  return e;

        _module.close();
        return 0;
    }
    else
    {
        if( !_module.open(_path.ptr()) )
            return ersIO_ERROR "dynamic library not found";

        dynarray< NODEREF<ServiceCoid> > svc;
        update_service_list(svc,0);

        run_services(svc);
        return 0;
    }
}

////////////////////////////////////////////////////////////////////////////////
opcd PluginCoid::update_core_services( dynarray< NODEREF<ServiceCoid> >& newsvc, AcceptSvcCbk* acc )
{
    load_services( &__get_sd, newsvc, acc );
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
uint PluginCoid::load_services( PluginCoid::t_coid_get_sdir ldr, dynarray< NODEREF<ServiceCoid> >& newsvc, AcceptSvcCbk* acc )
{
    uint nok=0;
    
    const ClassRegister<ServiceDescriptor>* sdir;
    sdir = ldr();

    if(!sdir)
    {
        post_alert(0) << "device " << _path << " failed to provide a service register" << EOM;
        return 0;
    }
    else
    {
        ClassRegister<ServiceDescriptor>::const_iterator a,b;
        a = sdir->begin();
        b = sdir->end();

        uint n=0,tn=0;
        for( ; a!=b; ++a )
        {
            ServiceDescriptor sd = *a;
            ++tn;

#ifdef _DEBUG
            if( !(sd._flags & ServiceDescriptor::fDEBUG) ) {
                post_info(0) << "ignoring service: " << sd._name << " contained within " << _path << " (not a debug version)" << EOM;
                continue;
            }
#else
            if( sd._flags & ServiceDescriptor::fDEBUG ) {
                post_info(0) << "ignoring service: " << sd._name << " contained within " << _path << " (a debug version)" << EOM;
                continue;
            }
#endif

            charstr wd = token(_path).cut_left_back( directory::separator() );
            directory::treat_trailing_separator( wd, true );

            opcd e=0;
            if(acc)
                e = acc->accept( sd, wd );
            if(e)
            {
                charstr ver;
                binstream* bin;
                if( e == ersREJECTED )
                    bin = &post_info(0);
                else
                    bin = &post_warning(0);

                *bin << "skipping service: " << sd._name << "-"
                    << sd._version.get_version(ver,true) << " error: " << opcd_formatter(e) << EOM;
                continue;
            }

            NODEREF<ServiceCoid> svcx;
            if( 0 != (e = SRVROOTA._objmgr.service_find_exact( sd._name, sd._version, svcx )) )
            {
                charstr ver,verl;
                binstream& msg = post_open() << "loading service: " << sd._name << "-"
                    << sd._version.get_version(ver,true)
                    << "\n  working directory: " << wd;
                if( sd._acceptor_port != 0  &&  sd._acceptor_port != UMAX32 )
                    msg << "\n  listener port: " << sd._acceptor_port;
                msg << EOM;

                if( !sd._coid_version.can_use_provider( GserverVersion, true ) )
                {
                    post_alert(0) << "service " << sd._name << " was compiled for coid version "
                        << sd._coid_version.get_version(ver,true) << " while the current version is "
                        << GserverVersion.get_version(verl,true) << EOM;
                    continue;
                }

                ++nok;

                ServiceCoid* svc = new ServiceCoid( sd, wd );

                NODEREF<ServiceCoid> res;
                SRVROOTA.attach( svc, res, _id, 0 );

                svc->_scit = _scit;
                ++n;
                *newsvc.add(1) = res;

                res.unset();
            }
            else
            {
                if( _scit == 0 ) {
                    charstr ver;
                    post_warning(0) << "service with the same name and version already loaded: "
                        << sd._name << "-" << sd._version.get_version(ver,true) << EOM;
                }

                svcx->_scit = _scit;         //update the scan iteration
                ++n;
            }
        }

        if( _scit == 0  &&  (n == 0 || n < tn) )
            post_warning(0) << n << " services loaded out of " << tn << " services exported from "
            << class_name() << ":" << object_name() << EOM;


        /*
        NODEREF<ServiceCoid> k;
        SRVROOTA._objmgr.get_node( _id, k );
        if( k.goto_first_of_class( ServiceCoid::GET_INTERFACE() ) )
        {
            for(;;)
            {
                if( ((ServiceCoid*)k->_node)->_scit != _scit )
                {
                    //post_close() << "removing service:" << k->_name << EOM;
                    post_info(0) << "missing service: \"" << k->_node->_name << "\"" << EOM;
                }

                if( !k.goto_next_of_class( ServiceCoid::GET_INTERFACE() ) )  break;
            }
        }
        */
    }

    return nok;
}

} // namespace coid
